#include "fileHandling.h"
//#include "vfs.h"
#include "hashtable.h"
#include "bst.h"

/*/////////////////////////////////////////////////////////////////////////////
~~~~~~~~~~~~~~~~~~~~~~~~~~~~//Function Definitions/~~~~~~~~~~~~~~~~~~~~~~~~~~~
////////////////////////////////////////////////////////////////////////////*/

int creatVFS(int fileSystemSize, char *fileSysName) {

    int i;
    FileDescriptor flDesc;
    Block blk;
    buffer buff;
    FILE *ptr;
    

char fileSystemName[fileSystemNameLength];
int noOfBuffers;
int noOfFileDescriptors;
int noOfFreeBlockListItems;
fileSize=1;  // size of 1 file, in terms of no. of buffer blocks
long fileDescOffset;
long bufferOffset;
long blockOffset;


if(strlen(fileSysName)>=31){
        printf("createvfs_FAILURE %s\n",ERR_VFS_CREATE_03);
        return createvfs_FAILURE;
    }


 strcpy(fileSystemName, fileSysName);
    
    //to check if the file name is valid
    //A file name cannot contain any of the following characters: \ / : * ? " < > |
    if(invalidChar(fileSystemName,0)==1){
        printf("createvfs_FAILURE %s\n",ERR_VFS_CREATE_04);
         return createvfs_FAILURE;
    }
    
    //if file size is invalid
    if(fileSystemSize<=0 || fileSystemSize>1024){
        printf("createvfs_FAILURE %s\n",ERR_VFS_CREATE_05);
         return createvfs_FAILURE;
    }
    
    
    blk.empty = Block_EMPTY;
    flDesc.type = '\0';
    flDesc.startBlock = 0;
    flDesc.size = 0;
    // fileSystemSize = 15*sizeof(char)+5*sizeof(int)+noOfFreeBlockListItems*sizeof(Block)
    //                 +noOfFileDescriptors*sizeof(FileDescriptor)+noOfBuffers*fileSize*sizeof(Buffer)
    //                 ;    

   //datafile already exists
    if ((ptr = fopen(fileSysName, "rb")) != NULL){
    printf("createvfs_FAILURE %s\n",ERR_VFS_CREATE_01);
    return createvfs_FAILURE;
    }
    //cannot create datafile
    if ((ptr = fopen(fileSysName, "wb")) == NULL) {
        printf("createvfs_FAILURE %s\n",ERR_VFS_CREATE_02);
        return createvfs_FAILURE;
    } else {

      // noOfBuffers = (fileSystemSize * 1024 - fileSystemNameLength * sizeof (char) - 5 * sizeof (int)) / (1024 * fileSize + (1 / fileSize) * sizeof (FileDescriptor) + sizeof (Block));
	
	noOfBuffers = (fileSystemSize * 1024 ) / (1024 * fileSize);

        noOfFreeBlockListItems = noOfBuffers;
        noOfFileDescriptors = noOfBuffers / fileSize;
    
        strcpy(fileSystemName, fileSysName);
        
    //	printf(" .%s,%s.%d \n",fileSystemName,fileSysName,strlen(fileSystemName));
        fwrite(&fileSystemName, sizeof (char),fileSystemNameLength, ptr);
        fwrite(&fileSystemSize, sizeof (int), 1, ptr);
        fwrite(&noOfFileDescriptors, sizeof (int), 1, ptr);
        fwrite(&noOfBuffers, sizeof (int), 1, ptr);
        fwrite(&noOfFreeBlockListItems, sizeof (int), 1, ptr);
        fwrite(&fileSize, sizeof (int), 1, ptr);

        blockOffset = ftell(ptr);// keeps the address of the starting of freeBlockListArray
        for (i = 0; i < noOfFreeBlockListItems; i++)
            fwrite(&blk, sizeof (Block), 1, ptr);

        fileDescOffset = ftell(ptr);// keeps the starting address of the fileDescriptorArray
        for (i = 0; i < noOfFileDescriptors; i++) {
            fwrite(&flDesc, sizeof (FileDescriptor), 1, ptr);
        }

        bufferOffset = ftell(ptr);// keeps the starting address of the bufferArray
        for (i = 0; i < noOfBuffers; i++)
            fwrite(&buff, sizeof (buffer), 1, ptr);

    
	strcpy(flDesc.name, "/");
            strcpy(flDesc.address, "/");
            flDesc.size = 0; // size of file or 0 if directory
            flDesc.startBlock = 0; // startAddress of buffer where this file is written or 0 if directory
           
        flDesc.type = 'd';


        // Write file descriptor to VFS

        fseek(ptr, fileDescOffset, SEEK_SET);
        fwrite(&flDesc, sizeof (FileDescriptor), 1, ptr);
        fclose(ptr);
	
	printf("createvfs_SUCCESS\n");

        return createvfs_SUCCESS;
    }
    
}

int mount(char* fileSysName) {

    FILE *ptr;
    int i = 0;
    FileDescriptor fDes;
   
    if(mountStatus==mounted){
        printf("mountvfs_FAILURE %s\n",ERR_VFS_MOUNT_04);
        return mountvfs_FAILURE;
    }
    
    if(strlen(fileSysName)>=31){
    
    fileSysName[30]='\0';
    }
    
    //if file doesn't exist //if file cannot be read
    if ( (ptr = fopen(fileSysName, "rb")) == NULL) {
        printf("mountvfs_FAILURE %s\n",ERR_VFS_MOUNT_01);
        return mountvfs_FAILURE;
    } else {

        fread(&fileSystemName, sizeof (char), fileSystemNameLength, ptr);
       
    //   printf(" .%s,%s.%d.%d \n",fileSystemName,fileSysName,strlen(fileSystemName),strlen(fileSysName));
       
        if(strcmp(fileSystemName,fileSysName)!=0){
            printf("mountvfs_FAILURE %s\n",ERR_VFS_MOUNT_02);
	    return mountvfs_FAILURE;
        }
        
        fread(&fileSystemSize, sizeof (int), 1, ptr);
        fread(&noOfFileDescriptors, sizeof (int), 1, ptr);
        fread(&noOfBuffers, sizeof (int), 1, ptr);
        fread(&noOfFreeBlockListItems, sizeof (int), 1, ptr);
        fread(&fileSize, sizeof (int), 1, ptr);

        blockOffset = ftell(ptr);
        
        fseek(ptr, sizeof (Block) * noOfFreeBlockListItems, SEEK_CUR);
        fileDescOffset = ftell(ptr);
        fseek(ptr, sizeof (FileDescriptor) * noOfFileDescriptors, SEEK_CUR);
        bufferOffset = ftell(ptr);
/*

        fseek(ptr, blockOffset, SEEK_SET);
        for(i=0;i<noOfFreeBlockListItems;i++){
        fread(&blk, sizeof (Block), 1, ptr);
        printf("%ld",blk.empty);
        }
        
*/	
	mountStatus=mounted;
        create_hash();
        fseek(ptr, fileDescOffset, SEEK_SET);
        for (i = 0; i < noOfFileDescriptors; i++) {
            fread(&fDes, sizeof (FileDescriptor), 1, ptr);
           
            if (fDes.type != '\0') {
//printf(" Name: %s Size : %ld Address : %s Type : %c fdOffset : %ld Offset : %ld", fDes.name, fDes.size, fDes.address, fDes.type, ftell(ptr) - sizeof (FileDescriptor), fDes.startBlock);
                noOfFiles++;
               mountNAry(&fDes, ftell(ptr) - sizeof (FileDescriptor));
	       insertFile(fDes.name, ftell(ptr) - sizeof (FileDescriptor));
		add_elt(fDes.address);
            }
        }
        fclose(ptr);
	
        printf("mountvfs_SUCCESS\n");
        return mountvfs_SUCCESS;
    }
    
   // printf("No. Of Files : %ld\n", noOfFiles);
}

int unMount(char* fileSysName){

    FILE *ptr;
    
    if(mountStatus==unMounted){
         printf("unmountvfs_FAILURE %s\n",ERR_VFS_UNMOUNT_02);
	 return unmountvfs_FAILURE;
     }
    
    if(fileSysName==NULL || strlen(fileSysName)==0 ){
         printf("unmountvfs_FAILURE %s\n",ERR_VFS_UNMOUNT_00);
	 return unmountvfs_FAILURE;
     }
    
    
    if(strcmp(fileSysName, fileSystemName)!=0){
        printf("unmountvfs_FAILURE %s\n",ERR_VFS_UNMOUNT_01);
        return unmountvfs_FAILURE; // Request to unMount file system that was not mounted, datafile not found
    }
    
     if ((ptr = fopen(fileSystemName, "rb+")) == NULL) {
         printf("unmountvfs_FAILURE %s\n",ERR_VFS_UNMOUNT_03);
	 return unmountvfs_FAILURE;
     }
     if(ptr!=NULL)
    fclose(ptr);
    setHead(NULL);
    nullHead();
    mountStatus=unMounted;
    printf("unmountvfs_SUCCESS\n");
    return unmountvfs_SUCCESS;
}

long updateFileDescriptor(char* destDirPath, char* Name, char fileType, int size, long offset) {

    FILE *wrtPtr;
    FileDescriptor fDes;
    long fdOffset = 0;

    if ((wrtPtr = fopen(fileSystemName, "rb+")) == NULL) {
   //     printf("File could not be opened by updateFileDescriptor\n");
	    return -5;
    } else {

        // Seek to Start of File Descriptor array
        fseek(wrtPtr, fileDescOffset, SEEK_SET);

        // Search empty File Descriptor
            for (fdOffset = 0; fdOffset < noOfFileDescriptors; fdOffset++) {
                fread(&fDes, sizeof (FileDescriptor), 1, wrtPtr);
                if (fDes.type == '\0') { // Search for empty file descriptor
                    break;
                }
            } 
        //All FDs are used up.
        if(fdOffset == noOfFileDescriptors)
        return -1;
        
        //Set Values in File Descriptor
            strcpy(fDes.name, Name);
            strcpy(fDes.address, destDirPath);
            fDes.size = size; // size of file or 0 if directory
            fDes.startBlock = offset; // startAddress of buffer where this file is written or 0 if directory
            noOfFiles++;
        
        

        fDes.type = fileType;


        // Write file descriptor to VFS

        fseek(wrtPtr, -sizeof (FileDescriptor), SEEK_CUR);
        fdOffset = ftell(wrtPtr);
        fwrite(&fDes, sizeof (FileDescriptor), 1, wrtPtr);
        
/*
                // Checking if writing was successful !
                printf("Checking if writing was successful !\n");
                fseek(wrtPtr, -sizeof (FileDescriptor), SEEK_CUR);
                fread(&fDes, sizeof (FileDescriptor), 1, wrtPtr);
                printf("Name : %s\n", fDes.name);
                printf("Address : %s\n", fDes.address);
                printf("Type : %c\n", fDes.type);
                printf("Size : %ld\n", fDes.size);
                printf("StartBlock : %ld\n", ftell(wrtPtr));
*/

        fclose(wrtPtr);

        return fdOffset;
    }
    return -1;
}

int updateFreeBlockList() {

    FILE *wrtPtr;
    Block blk;
    long offset = 0;
    if ((wrtPtr = fopen(fileSystemName, "rb+")) == NULL) {
      //printf("CANNOT_WRITE_TO_DATAFILE\n");
       return -2;
    } else {
        fseek(wrtPtr, blockOffset, SEEK_SET);

        // Check for empty block
        for (offset = 0; offset < noOfFreeBlockListItems; offset++) {
            fread(&blk, sizeof (Block), 1, wrtPtr);
            if (blk.empty == Block_EMPTY) {
                break;
            }
        }
        // No space in file system
        if(offset==noOfFreeBlockListItems)
            return -1;
        // Initialize block structure to write to VFS, updating prev. entry
        blk.empty = Block_FULL;
        fseek(wrtPtr, -sizeof (Block), SEEK_CUR);
        // Update free block value to block full
        fwrite(&blk, sizeof (Block), 1, wrtPtr);
        fclose(wrtPtr);
	return offset;
    }

   
}

int updateBuffer(int offset, char* dataFilePath) {
    FILE *rdPtr;
    FILE *wrtPtr;
    char ch = 'p';
    int size = 0;
    if ((rdPtr = fopen(dataFilePath, "rb")) == NULL) {
    //    printf("Data File could not be opened by updateBuffer\n");
	    return -5;
    } else if ((wrtPtr = fopen(fileSystemName, "rb+")) == NULL) {
   //     printf("FileSystem could not be opened by updateBuffer\n");
	    return -5;
    } else {
        // Seek to buffer area to write file
        fseek(wrtPtr, bufferOffset + offset * sizeof (buffer), 0);
    //    printf("Reached to update location : %ld\n", ftell(wrtPtr));
        while (!feof(rdPtr) && size<1024 ) { // Read file from HD and Write it to VFS
            fread(&ch, sizeof (char), 1, rdPtr);
            fwrite(&ch, sizeof (char), 1, wrtPtr);
            size++; //loop is running file size + 1 
            //  printf("%c",ch);             //times as it is reading EOF, writing 
        } //it to VFS and then checking if EOF is reached
	
	if(!feof(rdPtr))
		size=-1;
		
        fclose(rdPtr);
        fclose(wrtPtr);
	
        return size - 1;
    }
    return -3;
}

int setBlockFree(long offset,int mode) {

    FILE* updtPtr;
    Block blk;
    blk.empty = Block_EMPTY;
    if ((updtPtr = fopen(fileSystemName, "rb+")) == NULL) {
        printf("File could not be opened by setBlockFree\n");
    } else {
      //  printf("Clearing Block at : %ld", offset);
        if(mode==0)     // this is for normal file deletion
        fseek(updtPtr, blockOffset + ((bufferOffset - offset) / sizeof (buffer)) * sizeof (Block), SEEK_SET);
        else if(mode ==1)       // This is for resetting Block, in duplicate conflict occurred in addFile
        fseek(updtPtr, blockOffset + offset * sizeof (Block), SEEK_SET);    
        fwrite(&blk, sizeof (Block), 1, updtPtr);
        fclose(updtPtr);
        return 0;
    }
    return 1;
}

long setFileDescriptorFree(long offset) {

    FILE* updtPtr;
    FileDescriptor flDes;

    long blkOffset;
    if ((updtPtr = fopen(fileSystemName, "rb+")) == NULL) {
        printf("File could not be opened by setFileDescriptorFree\n");
    } else {
        fseek(updtPtr, offset, SEEK_SET);
        fread(&flDes, sizeof (FileDescriptor), 1, updtPtr);
        blkOffset = flDes.startBlock;
        flDes.type = '\0';
        fseek(updtPtr, -sizeof (FileDescriptor), SEEK_CUR);
        fwrite(&flDes, sizeof (FileDescriptor), 1, updtPtr);
        fclose(updtPtr);
        noOfFiles--;
        return blkOffset;
    }
    return 0;

}

